home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2001 / MacHack 2001.toast / pc / Papers / Garrison / Code / SchmoozingExamples / TCPCFSocket.m < prev    next >
Encoding:
Text File  |  2001-06-23  |  6.6 KB  |  201 lines

  1. //  TCPCFSocket.m
  2. //  Maxter
  3. //
  4. //  Created by bgg on Sat Nov 25 2000.
  5. //  Copyright (c) 2000 Object Craft P/L. All rights reserved.
  6.  
  7. #import "TCPCFSocket.h"
  8.  
  9. RCS_ID("$Id: TCPCFSocket.m,v 1.13 2000/12/08 05:47:40 bgg Exp $");
  10.  
  11. // Based on work from Douglas Johnston <doug@timeinc.net>.  Understanding the
  12. // CoreFoundation call backs was due to his efforts, bravo!
  13.  
  14. static void _tcpCFSocketCallback(CFSocketRef s, CFSocketCallBackType type,
  15.                                  CFDataRef address, const void *data,
  16.                                 void *info);
  17. const void *_tcpCFSocketRetainContext(const void *);
  18. void _tcpCFReleaseSocketContext(const void *);
  19.  
  20.  
  21. @implementation TCPCFSocket
  22.  
  23. - (void)dealloc
  24. {
  25.     if (_runLoopSource)
  26.         [self removeFromRunLoop];
  27.     [self _invalidateSocket];
  28.     [super dealloc];
  29. }
  30.  
  31.  
  32. // action methods
  33.  
  34. - (void)dataAvailableWithNotification;
  35. {
  36.     [self _addToRunLoopWithCallBackType:kCFSocketReadCallBack];
  37. }
  38.  
  39. - (void)acceptWithNotification
  40. {
  41.     [self _addToRunLoopWithCallBackType:kCFSocketDataCallBack];
  42. }
  43.  
  44. - (void)readWithNotification
  45. {
  46.     [self _addToRunLoopWithCallBackType:kCFSocketDataCallBack];
  47. }
  48.  
  49. // need to overload acceptConnection to close the socket fd held open
  50. // by the CFSocket
  51.  
  52. - (void)acceptConnection
  53. {
  54.     [super acceptConnection];
  55.     [self _invalidateSocket];
  56. }
  57.  
  58. // XXX add more? -writeWithNotification, etc ...?
  59.  
  60. // remove our listener from the run loop.
  61.  
  62. - (void)removeFromRunLoop
  63. {
  64.     if (_runLoopSource == NULL)
  65.         return;
  66.     CFRunLoopRemoveSource([[NSRunLoop currentRunLoop] getCFRunLoop],       
  67.                             _runLoopSource,
  68.                             kCFRunLoopDefaultMode);
  69.     if (CFRunLoopSourceIsValid(_runLoopSource))
  70.         CFRunLoopSourceInvalidate(_runLoopSource);
  71.     CFRelease(_runLoopSource);
  72.     _runLoopSource = NULL;
  73. }
  74.  
  75. // internal methods
  76.  
  77. // closes file descriptor in CFSocket
  78.  
  79. - (void)_invalidateSocket
  80. {
  81.     if (_cfSocketRef != NULL) {
  82.         if (CFSocketIsValid(_cfSocketRef))
  83.             CFSocketInvalidate(_cfSocketRef);
  84.         CFRelease(_cfSocketRef);
  85.         _cfSocketRef = NULL;
  86.     }
  87. }
  88.  
  89. // Add the CFSocket to the run loop this way:
  90.  
  91. - (void)_addToRunLoopWithCallBackType:(CFSocketCallBackType)callBackType
  92. {
  93.     _socketContext.version = 1;
  94.     _socketContext.retain = &_tcpCFSocketRetainContext;
  95.     _socketContext.release = &_tcpCFReleaseSocketContext;
  96.     _socketContext.copyDescription = NULL;
  97.     _socketContext.info = self;            // arg passed to call back
  98.  
  99.     _cfSocketRef = CFSocketCreateWithNative(NULL, [self socketFD], callBackType,
  100.                                             &_tcpCFSocketCallback, &_socketContext);
  101.     if (_runLoopSource != NULL) {
  102.         [NSException raise:TCPCFSocketException 
  103.                     format:@"_addToRunLoop:run loop active"];
  104.     }
  105.     _runLoopSource = CFSocketCreateRunLoopSource(NULL, _cfSocketRef, 0);
  106.     CFRunLoopAddSource([[NSRunLoop currentRunLoop] getCFRunLoop], 
  107.                         _runLoopSource,
  108.                         kCFRunLoopDefaultMode);
  109. }
  110.  
  111. // generate appropriate notification to alert caller that event has been
  112. // recieved.
  113.  
  114. - (void)_processCallBackType:(CFSocketCallBackType)type withData:(NSData *)data
  115. {
  116.     NSDictionary *ui;
  117.     ONTCPSocket *newSocket;
  118.     NSNotificationCenter *defCenter = [NSNotificationCenter defaultCenter];
  119.  
  120.     switch (type) {
  121.     case kCFSocketNoCallBack:        // no call back (how are we here?!)
  122.         [NSException raise:TCPCFSocketArgumentException format:@"impossible call back"];
  123.         break;
  124.     case kCFSocketReadCallBack:        // data available or ready to accept
  125.         [self removeFromRunLoop];
  126.         [defCenter postNotificationName:TCPCFSocketDataAvailableNotification
  127.                                  object:self];
  128.         break;
  129.     case kCFSocketAcceptCallBack:    // acception connected, new fd in data
  130.         // XXX this is RONG!
  131.         [NSException raise:TCPCFSocketException format:@"XXX not implemented (yet)"];
  132.         // newSocket = [ONTCPSocket socketWithFD:*(const int *)[data bytes]];
  133.         newSocket = nil;
  134.         ui = [NSDictionary dictionaryWithObjectsAndKeys:
  135.                                         newSocket, TCPCFSocketNotificationONTCPSocketItem,
  136.                                         nil];
  137.         [defCenter postNotificationName:TCPCFSocketAcceptedNotification
  138.                                  object:self
  139.                                userInfo:ui];
  140.         break;
  141.     case kCFSocketDataCallBack:        // data read from socket
  142.         ui = [NSDictionary dictionaryWithObjectsAndKeys:
  143.                                         data, TCPCFSocketNotificationDataItem,
  144.                                         nil];
  145.         [defCenter postNotificationName:TCPCFSocketReadCompletionNotification
  146.                                  object:self
  147.                                userInfo:ui];
  148.         break;
  149.     default:
  150.         [NSException raise:TCPCFSocketArgumentException format:@"unknown CFSocket call back type: %d", type];
  151.         break;
  152.     }
  153. }
  154. @end
  155.  
  156.  
  157. // call-back function from CFRunLoop to handle socket activity.  We convert
  158. // the passed object into an NSData and then deal with those details by
  159. // calling a method in the instance of TCPCFSocket which requested this
  160. // callback; the instance's address is passed as "info".
  161. //
  162. // I suspect that data already is an "NSData *" but anyway we can't tell that
  163. // from here.  Oh well.
  164.  
  165. static void
  166. _tcpCFSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address,
  167.                                         const void *data, void *info)
  168. {
  169.     NSData *dataObj;
  170.     char *buf;
  171.  
  172.     if (data != NULL) {
  173.         if ((buf = NSZoneMalloc(NSDefaultMallocZone(), CFDataGetLength(data))) == 0)
  174.             [NSException raise:NSMallocException format:@"_tcpCFSocketCallback"];
  175.         CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), buf);
  176.         dataObj = [NSData dataWithBytesNoCopy:buf length:CFDataGetLength(data)];
  177.     } else
  178.         dataObj = nil;
  179.  
  180.     [(TCPCFSocket *)info _processCallBackType:type withData:dataObj];
  181. }
  182.  
  183. const void *
  184. _tcpCFSocketRetainContext(const void *info)
  185. {
  186.     return info;
  187. }
  188.  
  189. void
  190. _tcpCFReleaseSocketContext(const void *info)
  191. {
  192.     // (do nothing)
  193. }
  194.  
  195. NSString *TCPCFSocketException = @"TCPCFSocketException";
  196. NSString *TCPCFSocketArgumentException = @"TCPCFSocketArgumentException";
  197. NSString *TCPCFSocketDataAvailableNotification = @"TCPCFSocketDataAvailableNotification";
  198. NSString *TCPCFSocketAcceptedNotification = @"TCPCFSocketAcceptedNotification";
  199. NSString *TCPCFSocketReadCompletionNotification = @"TCPCFSocketReadCompletionNotification";
  200. NSString *TCPCFSocketNotificationONTCPSocketItem = @"TCPCFSocketNotificationONTCPSocketItem";
  201. NSString *TCPCFSocketNotificationDataItem = @"TCPCFSocketNotificationDataItem";